package net.oschina.bilbodai.common.beanutil.bean;

import net.oschina.bilbodai.common.beanutil.exception.PropertyDumpException;
import net.oschina.bilbodai.common.beanutil.property.PropertyUtil;
import net.oschina.bilbodai.common.beanutil.util.TypeUtil;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Type;

import java.lang.reflect.Method;

import static org.objectweb.asm.Opcodes.CHECKCAST;
import static org.objectweb.asm.Opcodes.INVOKESTATIC;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;

/**
 * @author daimao  Date: 16/4/21 Time: 下午7:20
 * @version $Id$
 */
public class AttrPathQueryInfo {

    private static Method getProperty;

    static {
        try {
            getProperty = PropertyUtil.class.getDeclaredMethod("getProperty", String.class, Object.class);
        } catch (NoSuchMethodException e) {
            throw new PropertyDumpException("Method Not Found!", e);
        }
    }

    //如果Target的属性值需要调用PropertyUtils根据path去SrcBean找的情况下设置
    private final String propertyCallPath;

    private final String propertyTypeInternalName;

    private final String propertyDesc;

    private final Class<?> propertyType; // 通过pathQuery得到的属性的类型,比如 foo.name, 那么propertyType就是String.class

    private AttrPathQueryInfo(Class<?> propertyType, String propertyCallPath) {
        this.propertyType = propertyType;
        this.propertyTypeInternalName = Type.getInternalName(propertyType);
        this.propertyDesc = Type.getDescriptor(propertyType);
        this.propertyCallPath = propertyCallPath;
    }

    public static AttrPathQueryInfo create(Class<?> propertyType, String propertyCallPath) {
        return new AttrPathQueryInfo(propertyType, propertyCallPath);
    }

    public Class<?> getPropertyType() {
        return propertyType;
    }

    public String getPropertyCallPath() {
        return propertyCallPath;
    }

    public String getPropertyTypeInternalName() {
        return propertyTypeInternalName;
    }

    public void applyPath(MethodVisitor mv) {
        mv.visitLdcInsn(propertyCallPath);
    }

    public void applyInvoke(MethodVisitor mv) {
        mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(PropertyUtil.class), getProperty.getName(), Type.getMethodDescriptor(getProperty), false);
        TypeUtil.TypeHolder typeHolder = TypeUtil.getBoxingTypeByInternalName(propertyTypeInternalName);
        if (typeHolder != null) {
            mv.visitTypeInsn(CHECKCAST, typeHolder.getBoxTypeInternalName());
            mv.visitMethodInsn(INVOKEVIRTUAL, typeHolder.getBoxTypeInternalName(), typeHolder.getConvertMethod(), "()" + this.propertyDesc, false);
        } else {
            mv.visitTypeInsn(CHECKCAST, propertyTypeInternalName);
        }
    }

    public void preCheck(Class<?> beanType) {
        try {
            PropertyUtil.create(propertyCallPath, beanType); // 在第一次生成的时候就把实例创建出来并缓存,可以加快后面的执行
        } catch (Exception e) {
            throw new PropertyDumpException("Making AttrPathQuery Error! type=" + beanType + ",path=" + propertyCallPath, e);
        }
    }
}